package top.yxf.spring.aop.framework.autoproxy;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import top.yxf.spring.aop.*;
import top.yxf.spring.aop.aspectj.AspectJExpressionPointcutAdvisor;
import top.yxf.spring.aop.framework.ProxyFactory;
import top.yxf.spring.beans.BeansException;
import top.yxf.spring.beans.factory.BeanFactory;
import top.yxf.spring.beans.factory.BeanFactoryAware;
import top.yxf.spring.beans.factory.config.InstantiationAwareBeanPostProcessor;
import top.yxf.spring.beans.factory.support.DefaultListableBeanFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;

/**
 * @author xs.wu
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * 在目标 bean 被实例化之前应用这个 BeanPostProcessor。返回的 bean 对象可能是要使用的代理而不是目标 bean，
     * 从而有效地抑制了目标 bean 的默认实例化。如果这个方法返回了一个非空的对象，那么bean的创建过程就会短路。
     * 唯一应用的进一步处理是来自配置的 BeanPostProcessors 的 postProcessAfterInitialization 回调。
     * 此回调将应用于 bean 定义及其 bean 类，以及工厂方法定义，在这种情况下，返回的 bean 类型将在此处传递。
     * 后处理器可以实现扩展的 SmartInstantiationAwareBeanPostProcessor 接口，以预测它们将在此处返回的 bean 对象的类型。默认实现返回 null。
     * @param beanClass
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (isInfrastructureClass(beanClass)) {
            return null;
        }
        Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
        for(AspectJExpressionPointcutAdvisor advisor : advisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(beanClass)) {
                continue;
            }
            AdvisedSupport advisedSupport = new AdvisedSupport();
            TargetSource source = null;
            try {
                source = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
            } catch (Exception e) {
                e.printStackTrace();
            }
            advisedSupport.setTargetSource(source);
            advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
            advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            advisedSupport.setProxyTargetClass(false);

            return new ProxyFactory(advisedSupport).getProxy();
        }
        return null;
    }

    /**
     * 判定此 Class 对象所表示的类或接口与指定的 Class 参数所表示的类或接口是否相同，
     * 或是否是其超类或超接口。如果是则返回 true；否则返回 false。如果该 Class 表示一个基本类型，
     * 且指定的 Class 参数正是该 Class 对象，则该方法返回 true；否则返回 false。
     * @param beanClass
     * @return
     */
    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) || Pointcut.class.isAssignableFrom(beanClass) || Advisor.class.isAssignableFrom(beanClass);
    }
}
